La aprobación de la cursada estará dada por la aprobación de cuatro evaluaciones en máquina presenciales:
Si se desea mejorar la nota se podrá realizar una extensión del trabajo realizado en la cursada. La nota final se promediará con el desempeño durante la cursada
Además, programar en C, motiva la adopción de buenas prácticas de programación
Asumiendo el archivo se llama hola_mundo.c
#include <stdio.h>
int main()
{
printf("Hola Mundo\n");
return (0);
}
$ gcc hola_mundo.c -o hola_mundo -Wall
Tipo | Descripción | 32bits | 64bits |
---|---|---|---|
char |
caracter | 8 | 8 |
short |
entero corto con signo | 16 | 16 |
int |
entero con signo | 32 | 32 |
long |
entero largo con signo | 32 | 64 |
Tipo | Descripción | 32bits | 64bits |
---|---|---|---|
float |
flotante simple | 32 | 32 |
double |
flotante doble | 64 | 64 |
pointer |
puntero a una posición de memoria | 32 | 64 |
void |
idem char . Empleado para tipos genéricos |
- | - |
No existe el tipo boolean
La expresión:
3.45 – 1.2e+34
Significa 3.45 - (1.2*1034)
Base | Representación |
---|---|
Decimal | 14 |
Hexadecimal | 0xE |
Octal | 016 |
Determinan si el entero es con signo, corto o largo:
Literal | Representación | Significado |
---|---|---|
23L |
decimal | 23 long int |
23LU |
decimal | 23 unsigned long int |
023LU |
octal | 19 unsigned long int |
0XFUL |
hexadecimal | 15 unsigned long int |
char
o int
(valor ASCII) de forma indistintachar |
Significado |
---|---|
'A' |
Letra A mayúscula |
'\n' |
Salto de linea. Equivale a '\x0A'
|
'\0' |
Fin de string o NULL o cero |
'\x0A' |
Idem '\n' en hexadecimal |
'\012' |
Idem '\n' en octal |
'A'
no es lo mismo que "A"
['A','\0']
Se utilizan para enumerar una lista de valores constantes
enum estado_civil {casado, soltero, viudo, separado, divorciado, NSNC,
estado_civil_TOPE };
enum meses { ENE=1, FEB, MAR, ABR, MAY, JUN, JUL, AGO, SEP, OCT, NOV, DIC,
meses_TOPE };
int x; /* Variable global */
int main() {
int y; /* Variable automatica */
char *str; /* Variable automatica cuyo valor es la posición
* de memoria de otra variable
*/
str = malloc(50); /* Es en esta instancia donde se aloca memoria
* en la heap
*/
...
}
int main( ) {
int v_entero;
int *p;
v_entero = 7;
p = &v_entero;
...
&
permite acceder a la dirección de la variableEs posible realizar la incialización en la declaración
int x = 9;
int j, x = 12;
int j = 12, x;
char c = 'x';
const int x = 12;
char c, linea[80];
char nombre[] = "Juan";
const char mensaje[] = "Saludos!!";
Recordar que las últimas dos cadenas son arreglos de char terminadas en '\0'
['J','u','a','n','\0']
['S', 'a', 'l', 'u', 'd', 'o', 's', '!', '!', '\0']
(tipo) expresion
lvalue = rvalue
int
a char
se eliminan los bits de orden superiorfloat
a int
se trunca la parte fraccionariadouble
a float
se aplica redondeolong
) se convierten en short
o en char
eliminando los bits de orden superiorlong double
, se convierte el otro a long double
double
, se convierte el otro a double
float
, se convierte el otro a float
long
, se convierte el otro a long
int
, se convierte el otro a int
char
a short
Asumiendo el siguiente código
int x = 64, y = 357;
float f = 4.95;
char c;
c = x;
x = c;
c = y;
y = c;
y = f;
¿Con qué valores quedan x, y, f, c?
=
retorna el valor asignado, de forma tal de permitir
asignaciones encadenadas+
, -
, *
, /
, %
&&
, ||
!
==
, !=
,<
, >
,..
Cualquier valor distinto de 0 (cero) es VERDADERO. El 0 (cero) es FALSO
Es común usar de forma equivocada el símbolo =
como comparación
int x = 3;
if ( x = 3 ) /* siempre será true */
if ( x == 3 ) /* Uso correcto */
Se definen los operadores +=
, -=
, *=
, /=
, etc
Permiten realizar una asignación comprimida
x += 6; /* equivale a x = x + 6 */
Permiten incrementar/decrementar una variable en 1
Puede ser en forma prefija o postfija:
x++;
++x;
/* Ambos equivalen a x = x + 1 */
¿Con qué valores quedan?
int n = 5, x, y;
x = ++n;
y = n++;
El operador condicional if then else: _?_:_
e1 ? e2 : e3
El operador sizeof
que devuelve el tamaño de un tipo o variable
int x=2;
sizeof(x);
sizeof(int);
&
(AND), |
(OR), ^
(OR exclusivo), <<
, >>
(desplazamientos de bits a derecha o
izquierda), ~
(complemento a 1)&&
y con ||
() [] ->
! ~ ++ -- - (tipo) * & sizeof
* / %
+ -
<< >>
== !=
continúa...
&
^
|
&&
||
?
= += -= *= /=
,
if
, for
, while
, etc{ }
;
{
int x = 12;
y = x*2;
}
if (condicion) {
sentencias_verdad
} else {
sentencias_falso
}
if (x == 3) {
...
} else {
...
}
if (x != 0) {
...
}
if (x == 0) {
...
}
if (x) {
...
}
if (!x) {
...
}
switch (expresión) {
case expresion-constante: sentencias
case expresion-constante: sentencias
case expresion-constante: sentencias
default: sentencias
}
...
switch (letra) {
case 'a': case 'e': case 'i': case 'o': case 'u':
vocMin++;
break;
case 'A': case 'E': case 'I': case 'O': case 'U':
vocMay++;
break;
default:
otros++;
break;
}
...
Descargar ejemplo
while (expresión)
sentencias
int suma=0,n=10;
while (n--) {
suma += n;
....
}
/* Similar a
while (n) {
n = n - 1;
suma = suma + n;
...
*/
do
sentencias
while (expresión)
int sumo = 0, n = 10;
do
sumo += n
while (n--);
Descargar ejemplo
echo $?
for (expresión1; expresión2; expresión3)
sentencias;
expresión1;
while (expresión2) {
sentencias;
expresión3
}
int suma=0,n;
for( n=1; n <=10;n++)
suma+=n;
int suma=0, n=5;
for( ; n <=10;suma+=n, n++);
int suma=0, n=1;
for( ; n <=10; ) {
suma+=n;
n++;
}
main()
tipo_retornado nombre_función(lista de_argumentos)
{
declaraciones_y_sentencias;
[return expresion;]
}
int
void
int sumo_n (int n)
{
int suma=0;
for (; n>0; suma += n, n--);
return suma;
}
#include <stdio.h>
int main()
{
printf (“Hola Mundo”);
return 0;
}
void
para indicar el no retorno de valor por una función y una lista de argumentos vacía
tipo nombre_funcion (tipo_p1, tipo_p2, tipo_p3)
stdio.h
de la biblioteca estándardSTDIN
: Standard InputSTDOUT
: Standard OutputSTDERR
: Standard ErrorSTDIN
: se puede redirigir con <
STDOUT
: se puede redirigir con >
STDERR
: se puede redirigir con 2>
int getchar(void);
int putchar(int c);
getchar()
devuelve un entero pero se puede asignar a un char. Esto es porque
devuelve EOF
cuando se produce un error o alcanza el fin de archivoEOF
es -1
que no es un valor válido para un char.
putchar(int) envía a la salida estándar el caracter escrito. Envía el byte menos significativo.
getchar()
toma los datos de un buffer intermedio, temportal, que se carga
con lo que ingresa el usuario desde teclado (STDIN
)
Analizar stty -icanon
Si un programa presenta en pantalla la siguiente pregunta:
¿Desea ver el informe de nuevo (presione S/N)? _
S
, hasta que no se presione ENTER
no pasan los datos ingresados del buffer al programa
S
y el ENTER
getchar()
lee de a un carácter y si ingresa más de uno quedan esperando
en la cola de entradastty -icanon && cat
stty icanon
cat
#include <stdio.h>
int main(void)
{
int c;
printf("\nIngrese un caracter ");
c = getchar();
while (c != EOF) {
putchar(c);
c=getchar();
}
return 0;
}
int c
se debe a que EOF
es -1. Si fuese char un
valor no podría representarse
getchar()
y putchar()
trabajan caracter a caracter.
#include <stdio.h>
int main(void)
{
int c;
while ( (c=getchar()) !=EOF) {
putchar(c);
}
return 0;
}
int printf(const char *cadena_de_control, ...)
int scanf(const char *cadena_de_control, ...)
En las entradas y salidas con formato, se pueden agregar caracteres de conversión.
int printf(formato, arg1, arg2, ...)
int dia = 6, a = 8;
float var_flotante = 2.52345;
char mes[]="abril";
printf("Estamos en la clase de C");
printf("Hoy es lunes %d de %s", dia, mes);
printf("Ejemplo var_flotante = %0.2f", var_flotante);
Cada especificación de conversión comienza con un % y termina con un carácter de conversión.
Entre estos dos caracteres hay otros modificadores que deben especificarse en un orden determinado
A continuación detallamos estos modificadores
Char | Tipo | Se imprime como |
---|---|---|
d,i |
int |
Número decimal |
o |
int |
Octal sin signo (sin el cero inicial |
x,X |
int |
Hexadecimal |
u |
int |
Decimal sin signo |
c |
int |
Caracter |
s |
char * |
Cadena de caracters |
f |
float |
Número flotante |
#include<stdio.h>
int main(void)
{
int i;
printf("%8s %8s %8s\n", "^1", "^2", "^3");
for (i=1; i<6; i++) printf("%8d %8d %8d\n", i, i*i, i*i*i);
return 0;
}
Lee datos de STDIN
int scanf(const char *cadena_ctrl, …);
/* Usada generalmente de la siguiente forma */
scanf(tipo, &var)
Retorna el número de datos a los cuales se les ha asignado un valor con éxito
#include <stdio.h>
int main(){
int x, y, res;
printf("\nIngrese dos valores enteros separados por -,"
"luego presione enter:");
res = scanf("%d-%d", &x, &y);
printf("\nSe leyeron: \n\t1\t=>\t%d\n\t2\t=>\t%d\n"
"El resultado del scanf fue: %d\n", x, y, res);
return 0;
}
#include <stdio.h>
int main(){
int val;
do {
printf("\nIngrese un número entero (-1 termina, "
"letras se descartan): ");
while (scanf("%d", &val) != 1) {
fprintf(stderr, "Error. Intente nuevamente: ");
getchar();
}
printf("Se leyó: %d", val);
} while(val != -1);
return 0;
}
Ejemplo | Descripcion |
---|---|
scanf("%c%c%c", &a, &b, &c); |
Ingresando "x y" queda a con x, b con blanco y c con y |
scanf("%s", str); |
Ingresando "hola mundo" queda hola |
#include
#define
#undef
#if
#else
#endif
#ifdef
#ifndef
#elif
#include "nombr"
#include <nombre>
#include <stdio.h>
#include "../parent_subdir/my_header.h"
gcc -o prog archi1.c archi2.c
#define nombre textoReemplazo
#define MAX 100
En ejecución no ocupa un lugar en la memoria porque se reemplaza por una constante
#include
#define MAX =100
#define MIN 10;
if (x == MAX) /* if (x == =100) */
if (x == MIN) /* if (x == 10;) */
#define C A+B
D = C * C /* A+B * A+B */
#define C (A+B)
También permite definir macros personalizadas
#define abs(x) ( (x) > 0 ? (x) : -(x) )
z = abs(a - 3); /* z = ( (a-3) > 0 ? (a-3) : -(a-3) ); */
abs(a++);
#define swap(x,y) {int temp=x; x=y; y=temp;}
...
if (a>b)
swap(a,b)
else
...
¿Cuál sería un posible problema?
#define DOS 2
#define Doble(x) (DOS*(x))
#define Cuadruple(x) (Doble(Doble(x)))
....
z=Cuadruple(A);
....
z = (Doble(Doble(A)));
z = ((DOS*(Doble(A))));
z = ((DOS*((DOS*(A)))));
z = ((2*(DOS*(A))));
z = ((2*((2*(A)))));
#define X Y
#define Y Z
#define Z X
....
A=X;
....
/*
A = Y;
A = Z;
A = X
*/
#define sqrt(x) ( (x) < 0 ? 0 : sqrt(x) )
sqrt(-1);
sqrt(4);
__FILE__
Es una cadena de caracteres que contiene el nombre del archivo fuente__LINE__
El número de línea actual__DATE__
fecha de compilación__TIME__
hora de la compilación__STDC__
1, si compila ANSI C#
: si x
es un parámetro de una macro, #x
es el
parámetro actual correspondiente representado como
una cadena de caracteres.
#define comoString(x) #x
comoString(3) /* "3" */
comoString(a b) /* "a b" */
comoString("3") /* "\"3\"" */
##
: Se aplica cuando se procesa la macro y se reemplazan los
parámetros formales por los actuales. Los dos elementos que rodean al
operador se combinan
a + b##c /* a + bc */
#define concatenar(a,b) a##b
...
char prueba[] = "Hola mundo";
printf("%s", concat(pru,eba));
Si por alguna razón sucede que existe una macro y una función con el mismo nombre, resolvemos el conflicto usando paréntesis en las funciones:
#define cuadrado(x) (x)*(x)
double (cuadrado)(double x ){
return x*x + 1;
}
int main() {
printf("%.2f con macro\n", cuadrado(2.0));
printf("%.2f con funcion\n", (cuadrado)(2.0));
return 0;
}
Asumiendo los siguientes casos
const int MAX = 100 + 20;
#define MAX 100 + 20
En el uso MAX * MAX no se obtiene el mismo resultado
define
previo
#define MAX 12
..
#undef MAX
..
#define MAX 11
El preprocesador permite incorporar o eliminar sentencias de un código fuente según la evaluación de una expresión.
Es posible consultar por defined
#if #else #ifdef #ifndef #elif #endif
#if defined(MAX)
...
#endif
...
#ifdef MAX
...
#endif
#if DEBUG
...
#endif
#define ARG 0
#define GB 1
#define ESP 2
#define PAIS_ACTIVO ESP
#if PAIS_ACTIVO == ARG
char moneda[] = "pesos";
#elif PAIS_ACTIVO == GB
char moneda[] = "libras";
#else
char moneda[] = "euro";
#endif
#ifndef
#ifndef _HEADER
#define _HEADER
....
#ifndef MAX
#define MAX 100
#endif
...
#endif
#include <stdio.h>
#ifdef LINUX
#define MENSAJE "Estamos en Linux"
#else
#define MENSAJE "NO estamos en Linux"
#endif
int main(void){
printf(MENSAJE);
}
Podemos definir la macro fuera del fuente
gcc -o prog -DLINUX archi1.c
Es importante programarlos en forma correcta y adecuada para evitar comportamientos inesperados
Para declarar un puntero:
un_tipo * ptr; /* ptr es una variable de tipo puntero a un_tipo.
contiene la direccion de memoria de un dato de tipo
un_tipo */
int * var_int; /* para punteros a enteros */
double * var_double; /* para punteros a double */
char * var_char; /* para punteros a char */
Para desreferenciar una variable usamos &
int n=0; /* n es una variable de tipo int */
int * p; /* p es una variable de tipo puntero a un int,
que contendrá la dirección de un int */
p = &n; /* Se asigna a p el valor de la dirección de n */
Para referenciar el contenido de un puntero usamos *
*p
es equivalente al nombre de la variable n
*p = 33
es equivalente a n = 33
Supongamos el siguiente extracto de código:
int x = 5, y = 7, ptr1 = &x, ptr2 = &y;
Analizar gráficamente qué sucede con:
ptr1 = ptr2
*ptr1 = *ptr2
Analizando el siguiente código
void cambiar(int a, int b){
int aux;
aux = a;
a = b;
b = aux;
}
int main (){
int x = 10, y = 20;
cambiar(x, y);
}
Recordar que en C no exite el pasaje por referencia.
El código anterior no cambia nada
Si recordamos el uso del scanf
, vemos un ejemplo de cómo se simula el pasaje
por referencia
#include <stdio.h>
int main(){
int x; char mensaje[30];
scanf("%d", &x);
scanf("%s", mensaje);
return 0;
}
Pero.... ¿Mensaje no se precede con &?
un_tipo variable[TAM];
int x[12];
int num[] = { 1, 2 };
char cadena_1[128] = "Hola Mundo";
char cadena_2[] = "Hola";
int sum(int vec[], int dim);
Y devuelva todos estos valores
Son arreglos de arreglos
int i, j, matrix[10][20];
for(i = 0; i < 10; i++) {
for(j = 0; j < 20;j++) {
matrix[i][j] = 0;
}
}
Inicialización
int matrix_2[3][4] = { { 1, 2, 3, 4 },
{ 5, 6, 7, 8 },
{ 9, 10, 11, 12 } };
O alternativamente, pero NO RECOMENDADO
int matrix_3[3][4] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 };
void sample(int matrix[][10], int row_size);
Existe una fuerte relación entre punteros y arreglos
El nombre de un arreglo es una constante puntero
int my_array[] = {10, 20, 30, 40, 50};
printf("my_array[0] = %d", my_array[0]); /* Imprime: my_array[0] = 10 */
printf("*my_array = %d", *my_array); /* Imprime: *my_array = 10 */
Todo lo que se logra con indexación de arreglos, se logra con punteros
int my_array = { 1, 2 }
Variable | Posición | Contenido |
---|---|---|
my_array | 0xA000 | 0XA101 |
my_array[0] | 0xA101 | 1 |
my_array[1] | 0xA105 | 2 |
my_array[0] == *my_array
my_array[1] == *(my_array + 1)
Cualquier expresión escrita como arreglo e índice es equivalente a una expresión escrita como un puntero y un desplazamiento
int my_array[10], *ptr;
/*
my_array[i] equivale a *(my_array +i)
&my_array[i] equivale a (my_array +i)
ptr[i] equivale a *(ptr + i)
*/
p
y q
apuntan a elementos del mismo arreglo, entonces es correcto utilizar
los operadores ==, !=, <, >, <=, >=
p < q
es verdadero si p
apunta a un elemento que está antes en el arreglo de lo
que está el que apunta q
0
(== o !=)p + i;
p - i:
p
y q
apuntan a elementos del mismo arreglo, entonces está permitido la resta
entre p
y q
int array_a[] = {1, 2, 3, 4},
array_b[] = {10, 20, 30, 40};
a = b; /* NO ES POSIBLE */
a == b /* Devuelve true si a y b hacen referencia a la misma
* posición de memoria. No compara elementos
*/
0
(cero)0
(cero) automáticamente.
char cadena[11];
char cadena[]="Hola"; /* Constante string que equivale
* a {'H', 'o', 'l', 'a', '\0' }
*/
string.h
y strings.h
contienen las funciones
habituales para la gestión de strings
man string
string.h
stdlib.h
void *calloc(size_t nmemb, size_t size);
void *malloc(size_t size);
void *realloc(void *ptr, size_t size);
void free(void *ptr);
char * p = (char *) malloc(10 * sizeof(char));
int * q = (int *) calloc(10, sizeof(int)),
* dyn_array = NULL,
i;
free(p);
free (q);
for(i=1; i < 1000; i++) {
dyn_array = realloc(dyn_array, i * sizeof(int));
}
free (dyn_array);
Si recordamos la tabla de precedencia de operadores, podemos ver que las primeras líneas se enumeran así
() [] ->
! ~ ++ -- - (tipo) * & sizeof
* / %
+ -
<< >>
Continúa ...
La tabla va de mayor prioridad a menor prioridad
void some_function(** ptr) {
...
*ptr[i] = some_var;
...
}
Considerar la tabla de prioridades
Como las variables de tipo punteros son como cualquier otra, entonces podemos almacenarlas en un arreglo
char *lineas[5]; /* Arreglo de 5 punteros a char */
lineas[i]
es un puntero a char
*lineas[i]
es el primer carácter de la línea i-ésima
char * palabras [ ] = {"break", "continue", "do", "if", "int"};
palabras[0]
*palabras
palabras[1]
*(palabras +1)
palabras[1][2]
*(palabras [1] +2)
*(*(palabras +1)+2)
#include <stdio.h>
int main()
{
char *dias[]= { "lunes", "martes", "miercoles",
"jueves", "viernes", "sabado",
"domingo", };
int i;
for (i=0; i<7; i++) printf("El dia %d es %s\n",i,dias[i]);
return 0;
}
Analizar qué cantidad de espacio se necesita en cada caso:
int a[10][20];
int *b[10];
main
puede especificar dos parámetros
#include <stdio.h>
int main (int argc, char *argv[]){
int i;
for(i=0; i<argc; i++) printf("%s\n", argv[i]);
return argc;
}
struct etiqueta {
tipo nombre_miembro_1;
tipo nombre_miembro_2;
tipo nombre_miembro_3;
}
En el siguiente caso, se define la estructura y declaran dos variables en la misma línea
Luego se definen otras variables y realiza una asignación
struct t_student {
int file_number;
char name[256];
} student_1, student_2;
struct t_student student_3;
struct t_student student_4 = { 1234, "Juan Perez" };
student_2 = student_4;
+---------------------------------+-----------------+
| 256 bytes name | 4 bytes file_no |
+---------------------------------+-----------------+
El ejemplo es a modo ilustrativo dado que por cuestiones de alineación se utilizan estrategias diferentes. Ver ejemplo de alineación
Es posible definir estructuras sin nombre de tipo, especificando el nombre de la variable
struct {
int file_number;
char name[256];
} student_1, student_2;
El punto sirve para referenciar el miembro de una estructura
student_1.file_number;
student_2.name;
scanf("%s",student_3.name);
scanf("%d", &student_3.file_number);
printf("Student name %s (%d)",
student_3.name,
student_3.file_number);
struct t_address {
char address[256];
char city[256];
};
struct t_student {
int file_number;
char name[256];
float average_note;
struct t_address address;
}
struct t_student student_list[100];
...
for(i=0; i < 100; i++) {
printf("%d - %s",
student_list[i].file_number,
student_list[i].name);
}
struct t_month {
char * name;
unsigned int num_days;
} month[] = {
{"Ninguno", 0},
{"Enero", 31},
{"Febrero", 28},
.....
{"Noviembre", 30},
{"Diciembre", 31}
};
Los punteros a estructuras se utilizan como los punteros a cualquier otra variable.
struct t_student * ptr_student;
ptr_student = & student_1;
printf("%d - %s", (*ptr_student).file_number, (*ptr_student).name);
/* O equivalentemente */
printf("%d - %s", ptr_student->file_number, ptr_student->name);
Asumiendo los siguientes tipos de datos
struct t_static_student {
char name[256];
float average_note;
int file_number;
} student_1, student_2;
struct t_dinamyc_student {
char *name;
float average_note;
int file_number;
} d_student_1, d_student_2;
strcpy(student_1.name, "Juan");
student_1.average_note = 9.5;
student_1.file_number = 100;
student_2 = student_1
d_student_1.name = malloc(sizeof(char)*256);
strcpy(d_student_1.name, "Juan");
d_student_1.average_note = 9.5;
d_student_1.file_number = 100;
d_student_2 = d_student_1;
Analice qué sucede si modificamos student2.name
y d_student_2.name
student2 =
student1
#include <stdio.h>
int main()
{
#ifdef ARR
char a[10],b[10] = {'a','b',0};
a = b;
printf("b: %s\na: %s\n",b,a);
#else
struct { char arr[10];} a,b = { {'a', 'b', 0} };
a = b;
printf("b: %s\na: %s\n",b.arr,a.arr);
#endif
return 0;
}
Descargar ejemplo y compilar con -DARR
Asumiendo la siguiente estructura
struct t_example { char x; int y; float z; char s[10];} sample;
Las siguientes llamadas a función son válidas:
func1(sample.x); /* pasa el valor carácter de x */
func2(sample.y); /* pasa el valor entero de y */
func3(&sample.z); /* pasa la dirección de z */
func4(sample.s); /* pasa la dirección del string s */
func5(sample.s[2]); /* pasa el valor del carácter s[2] */
func6(e1); /* pasa la estructura completa por valor */
func7(&e1); /* pasa la estructura completa por ref */
typedef
no crea un nuevo tipo sino que crea un sinónimo para uno ya existente typedef previous_type defined_type;
typedef int t_age;
typedef struct {
int file_number;
char name[256];
} t_student;
t_age age;
t_student student;
Son estructuras que se definen recursivamente Se las utiliza para definir listas, árboles y grafos
struct t_node {
t_data data;
struct t_node *node;
}
typedef struct t_node * t_list;
void list_create(???);
short list_find(???, t_data);
short list_is_empty(???);
t_data list_add(???, t_data);
t_data list_delete(???, tdata);
void list_destroy(???);
void list_create(t_list *);
short list_find(t_list, t_data);
short list_is_empty(t_list);
t_data list_add(t_list *, t_data);
t_data list_delete(t_list *, tdata);
void list_destroy(t_list *);
El prototipo de un iterador sería:
t_list_iterator list_iterador_create(t_list );
void list_iterator_next(t_list_iterator *);
t_data list_iterator_data(t_list_iterator );
short list_iterator_end(t_list_iterator);
i = list_iterador_create(list);
while(! list_iterator_end(i)) {
t_data aux = list_iterator_data(i);
...
list_iterator_next( &i);
}
Podría usarse un for en vez de un while
Son variables que pueden tener distinto tipo en momentos diferentes de la ejecución de un programa
union {
int x;
float y;
char z[256];
} my_var;
union {
int x;
float y;
char z[256];
} my_var = { 10 };
struct shape {
int type;
double area;
union {
double side;
double ratio;
struct {
double height;
double width;
} rectangle;
} dimension;
} my_shape;
extern
static
register
Alcance: porción del programa donde una variable puede referenciarse por su nombre. En C depende de cómo fue declarada y si se le ha aplicado algún modificador
Tiempo de vida: tiempo durante el cual una variable tiene reservada memoria
#include <stdio.h>
int i = 0;
int main() {
float p = 9.0;
printf("%d %f\n", i, p)
return 0;
}
float z = 7.5;
void print_something(){
int j= 5;
printf("%d %f %d\n", i, z, j)
}
El alcance abarca desde la definición hasta el final del archivo
+----- int var_1;
| void function_1(int a ){
| int aux;
| ...
| }
|
| +-- int var2_;
| | void function_2(void ){
| | ..
| | }.
| |
| | +-int var3_;
| | | ...
| | |
V V V
¿Cómo se las puede referenciar fuera de ese alcance?
Referencia una variable que no está dentro del archivo fuente o se hace referencia antes de su definición
int var_1;
void function_1(){
extern int var_3;
...
}
int var_2;
void function_2(){
...
}
int var_3;
extern
cumplen que:
#include <stdio.h>
int cuento() {
static int x;
return x++;
}
int main() {
int i;
for(i=0 ; i<10 ; i++) printf("%d\n", cuento());
return 0;
}
x
se mantiene de llamada en llamada void f(register unsigned m){
register int i;
...
}
FILE *
definido en stdio.h
FILE *fp;
Ambas representaciones proveen funciones análogas
fopen
NULL
si hay algún error
fp = fopen(name, mode)
mode
puede ser: r, w, a, r+, w+, a+r
: solo lectura al iniciow
: trunca o crea un archivo. Se posiciona al principioa
: abre un archivo para añadir datos o lo crea si no existe. Se posiciona al finalr+
: abre como lectura y escritura. Se posiciona al principiow+
: abre como lectura y escritura, truncando el archivo si existe o
creandolo si no existe. Se posiciona al principioa+
: abre como lectura y escritura o lo crea si no existe. Se posiciona al final de archivo
En todos los casos puede agregarse una b
al final, pero se ignora en los
sistemas POSIX. En Linux no tiene sentido, pero se aconseja su uso cuando se
debe intercambiar datos con sistemas no UNIX
Los streams se cierran utilizando fclose
#include <stdio.h>
#include <errno.h>
#include <string.h>
extern int errno;
int main(int argc, char *argv[]) {
FILE *fp;
if (argc==2) {
if ((fp = fopen(argv[1], "r"))){
printf("El arch. %s pudo abrirse\n", argv[1]);
fclose(fp);
}
else printf("ERROR al abrir %s:=> %s\n", argv[1], strerror(errno));
}
else printf("Debe enviar nombre de archivo como parametro\n");
return 0;
}
STDIN
: solo de lecturaSTDERR
: solo de escrituraSTDOUT
: solo de escritura// De a bytes:
int fgetc(FILE *)
// De a lineas:
char * fgets(char *dato, int tam, FILE *fp)
// Con formato:
int fscanf(FILE *f, const char * format, ...)
// De a bloques:
size_t fread(void *dato, size_t tam, size_t cant, FILE * f)
// De a bytes:
int fputc(int c, FILE *)
// De a lineas:
int fputs(const char *dato, FILE *fp)
// Con formato:
int fprintf(FILE *f, const char * format, ...)
// De a bloques:
size_t fwrite(void *dato, size_t tam, size_t cant, FILE * f)
feof(FILE *)
i = 0;
while (!feof(fp)) {
fgets(buf, sizeof(buf), fp);
printf ("Line %4d: %s", i, buf);
i++;
}
Descargar ejemplo
| El código repite la última línea
| man fgets
awk 'BEGIN {i=1}{printf("Line %4d: %s\n", i++, $0) }'
Cuando se utiliza alguna función que manipule archivos, leer claramente el man para verificar
cuál es el valor de retorno. Entonces, en el caso de fgets
su uso correcto
sería:
i = 0;
while (fgets(buf, sizeof(buf), fp) != NULL) {
printf ("Linea %4d: %s", i, buf);
i++;
}
Descargar ejemplo fgets y feof
Descargar ejemplo fgetc y EOF
En las primeras clases vimos cómo implementar el comando cat, ahora podemos implementar el comando copy que copia dos archivos
void copy(FILE *in , FILE *out) {
int c;
while ((c=getc(in))!=EOF) fputc(c, out);
}
Estas funciones trabajan con texto formateado, por lo que trabajarán con archivos de texto y no binarios
void parse(FILE *in , FILE *out) {
int count;
float f1,f2,f3;
while ((count = fscanf(in,"%f,%f,%f", &f1, &f2, &f3)) ==3)
{
fprintf(out, "%.2f,%.2f,%.2f,%.2f\n", f1, f2, f3, f1+f2+f3);
}
if (!feof(in))
{
printf("Linea mal formada\n");
myclose(in,out);
exit(EXIT_FAILURE);
}
}
Descargar ejemplo fscanf y fprintf Probar con el ejemplo entregado
Estas funciones trabajan bajando la memoria tal cuál se almacena en ella, por esto generalmente trabajarán con archivos binarios y no de texto
Estas funciones permiten posicionarse en un archivo o determinar la posición actual del cursor en un archivo respectivamente
int fseek( FILE *flujo, long desplto, int origen);
/* Donde origen puede ser:
* * SEEK_SET: desde el inicio
* * SEEK_CUR: desde la posición actual
* * SEEK_END: desde el final
* fseek(fp, 0L, SEEK_CUR)
* fseek(fp, 0L, SEEK_SET)
* fseek(fp, 0L, SEEK_END)
*/
long ftell( FILE *flujo);
Operando con funciones de bajo nivel
unistd.h
int STDIN_FILENO
int STDOUT_FILENO
int STDERR_FILENO
Las funciones de apertura y cierre son:
int open(const char * nom, int flags, mode_t mode);
int close(int fd)
FILE * fdopen(int fd, const char * tipo);
int fileno(FILE * f);
ssize_t read(int fd, void * dato, size_t tam);
ssize_t write(int fd, const void * dato, size_t tam);
type (* function_name) [(args)]
int comprobar(const char *a, const char *b,
int (*cmp) (const char *, const char *)) {
return cmp(a,b);
}
typedef double (*ptr_function)(char *);
/* Donde:
* * double es el tipo de retorno de la función
* * La función recibe un puntero a char
* * El nombre del tipo es ptr_function
*/
ptr_function p_func = xxx;
double test = p_func("2.13");
Podemos simplemente definir arreglos de tipo punteros a función:
float (*operations[])(float, float) = { sum, minus, ... }
o usar typedef
para simplificar la lectura:
typedef float (*float_operation_t) (float, float);
float_operation_t operations[] = { sum, minus, ...}
El estandar dice:
void message (void (*func)(int), int times)
{
int j;
for (j=0; j<times; ++j)
func (j); /* (*func) (j); would be equivalent. */
}
void example (int want_foo)
{
void (*pf)(int) = &bar; /* The & is optional. */
if (want_foo)
pf = foo;
message (pf, 5);
}
stdlib
incluye la implementación del algoritmo de ordenamiento
Quick Sort
#include <stdlib.h>
void qsort(void *base, size_t nmemb, size_t size,
int (*compar)(const void *, const void *));
Desarrollar un algoritmo de manejo de lista o árbol genérico. Por genérico, entendemos que el mismo algoritmo sea independiente del dato que almacene
typedef struct node {
void * data;
struct node * left;
struct node * right;
} * t_node;
void
es un puntero que puede apuntar a cualquier
tipo de dato.void
pueda ser desreferenciado debe castearse antes de
ser utilizado.
int main() {
int var1 = 1;
double var2 = 1.0;
void* vptr ;
vptr = &var1;
*(int *) vptr = 2;
vptr = &var2;
*(float *) vptr = 4.0;
}
En la clase 6, vimos una implementación de un árbol binario de búsqueda implementado con enteros. Descargar ejemplo
Analizando printf
vemos que podemos usarla de cualquiera de las siguientes
formas:
printf("Hola Mundo");
printf("Hola %s", "Mundo");
printf("%s %s", "Hola", "Mundo");
int printf(const char * format, ...);
stdagr.h
printf
int my_variable_args(arg1, arg2, argN, ...);
va_start
va_arg
va_end
va_list
que permite declarar una variable que itere
sobre cada argumento. void va_start(va_list ptr, last_arg);
type va_arg(va_list ptr, type);
void va_copy(va_list target, va_list source);
void va_end(va_list ptr);
va_start
va_start
inicializa la lista para acceder a cualquier argumento variableva_arg
se utiliza luego para tomar los argumentos, siendo type el tipo del
siguiente argumentova_end
para restaurar la pilaLa familia de funciones que se ofrecen por stdio
considera:
int vprintf(const char *format, va_list ap);
int vfprintf(FILE *stream, const char *format, va_list ap);
int vsprintf(char *str, const char *format, va_list ap);
int vsnprintf(char *str, size_t size, const char *format, va_list ap);